import os
import time
import datetime
import copy

OneSecToNs = 1000000000  # nanosecond


DETECTOR_INTERVAL = 0.5 * OneSecToNs


RAW_INFO_SAVE_ROOT_PATH = os.path.expanduser('~') + "/order_detector_log_raw"
ABNORMAL_OPEN_SAVE_ROOT_PATH = os.path.expanduser('~') + "/order_detector_log"
ABNORMAL_CLOSE_SAVE_ROOT_PATH = os.path.expanduser('~') + "/order_detector_log_raw"

ABNORMAL_OPEN_ORDER_STORAGE_REFRESH_TIME = OneSecToNs * 1

influxdb_token = "d37FqY39jpcxuSmthcMjmBnbCsWiwxk0yjpkLPjWzIANgY7ro5iNpuN-Hwcg3JObKFJpPPIBWito_yLVt4hkWA=="
influxdb_org = "order"
influxdb_bucket = "order_detector"
influxdb_url = "http://35.79.11.23:8086"
influxdb_bucket_order_filter = "order_filter"


def get_exception_order_time():
  return 1 * OneSecToNs


def get_exchange_rate_totol(exchange: str) -> int:  # 1min
  if exchange == "Binance":
    return 1200
  else:
    return 1200


class QuerySpec():
  def __init__(self, *, strategy_name: str, exchange: str, market_type: str, api_version: str, owner: str, key_path: str) -> None:
    self._api_version = api_version
    self._strategy_name = strategy_name
    self._exchange = exchange
    self._market_type = market_type
    self._owner = owner
    self._key_path = key_path
    self._native_symbols = []
    self._ip = None
    self._machine = ""

  def set_ip(self, ip: str):
    self._ip = ip

  def set_native_symbols(self, native_symbols: list):
    self._native_symbols = copy.deepcopy(native_symbols)

  @property
  def ip(self):
    return self._ip

  @property
  def api_version(self):
    return self._api_version

  @property
  def strategy_name(self):
    return self._strategy_name

  @property
  def exchange(self):
    return self._exchange

  @property
  def owner(self):
    return self._owner

  @property
  def market_type(self):
    return self._market_type

  @property
  def key_path(self):
    return self._key_path

  @property
  def native_symbols(self) -> list:
    return self._native_symbols

  @property
  def machine(self) -> str:
    return self._machine

  @property
  def mea(self) -> str:
    mea = self.market_type + '.' + self.exchange + '.' + self.api_version
    return mea

  def spce(self) -> dict:
    spce = {}
    spce["mea"] = self.mea
    spce["strategy_name"] = self.strategy_name
    spce["machine"] = self.machine
    spce["owner"] = self._owner
    return spce


class OrderContext():
  def __init__(self, order_id: str, capture_time: int, order_info: dict) -> None:
    self._order_id = order_id
    self._record_list = []
    self._capture_time = capture_time
    self._last_record_time = None
    self.update(order_info, capture_time)

  @property
  def order_id(self) -> str:
    return self._order_id

  def update(self, order_info: dict, fetch_time: int):
    assert (len(str(time.time_ns())) == len(str(fetch_time)))
    self._last_record = order_info
    self._record_list.append(order_info)
    self._last_record_time = fetch_time

  @property
  def stay_time_ns(self) -> int:
    return self._last_record_time - self._capture_time

  @property
  def stay_time_sec(self) -> int:
    return (self._last_record_time - self._capture_time) // OneSecToNs

  @property
  def last_record_time(self) -> str:
    last_recode_time = datetime.datetime.utcfromtimestamp(self._last_record_time // OneSecToNs)
    return f"{last_recode_time}"

  @property
  def stay_time_for_human(self) -> str:
    capture_time = datetime.datetime.utcfromtimestamp(self._capture_time // OneSecToNs)
    last_time = datetime.datetime.utcfromtimestamp(self._last_record_time // OneSecToNs)
    return f"{last_time - capture_time}"

  def get_order_summary(self) -> dict:
    summary = {}
    summary["stay_time_ns"] = self.stay_time_ns
    summary["stay_time_human"] = self.stay_time_for_human
    summary["feach_time"] = self._capture_time
    summary["id"] = self._order_id
    summary["last_query_time"] = self._last_record_time
    summary["order_info"] = self._last_record
    return summary


class OpenOrderElement():
  def __init__(self, order_id: str, raw_info: dict, fetch_time: int) -> None:
    self._order_id = order_id
    self._raw_info = raw_info
    assert (len(str(time.time_ns())) == len(str(fetch_time)))
    self._fetch_time = fetch_time

  @property
  def fetch_time(self):
    return self._fetch_time

  @property
  def order_id(self):
    return self._order_id

  @property
  def raw_info(self):
    return self._raw_info


class StratDetectorContext:
  def __init__(self, query_spec: QuerySpec) -> None:
    self._query_spec = query_spec
    self._open_order_map = {}
    self._last_query_time = 0
    self._abnormal_open_order_list = []
    self._abnormal_close_order_list = []
    self._submitted = False

  def set_query_spec(self, query_spec: QuerySpec):
    self._query_spec = query_spec
  
  @property
  def query_spec(self) -> QuerySpec:
    return self._query_spec

  @property
  def last_query_time(self) -> int:
    return self._last_query_time

  def set_last_query_time(self, time: int):
    self._last_query_time = time

  def update_by_element(self, ele_list: list, update_time):
    self._abnormal_open_order_list = []
    self._abnormal_close_order_list = []
    
    open_id_set = set()
    self._last_query_time = update_time
    
    ele_min_time = update_time    
    for ele in ele_list:
      assert (isinstance(ele, OpenOrderElement))
      if ele._order_id in self._open_order_map:
        self._open_order_map[ele._order_id].update(ele.raw_info, ele.fetch_time)
      else:
        self._open_order_map[ele._order_id] = OrderContext(
            ele._order_id, ele.fetch_time, ele.raw_info)

      if ele_min_time > ele.fetch_time:
         ele_min_time = ele.fetch_time
      
      open_id_set.add(ele.order_id)

    del_set = set()
    for order_id, oc in self._open_order_map.items():
      assert (isinstance(oc, OrderContext))
      if oc._last_record_time < ele_min_time:
        del_set.add(order_id)
        if oc.stay_time_ns > get_exception_order_time():
          self._abnormal_close_order_list.append(oc)
      else:
        if oc.stay_time_ns > get_exception_order_time():
          self._abnormal_open_order_list.append(oc)

    for order_id in del_set:
      del self._open_order_map[order_id]

    self.set_last_query_time(time.time_ns())

  @property
  def abnormal_open_order_list(self):
    return self._abnormal_open_order_list

  @property
  def abnormal_close_order_list(self):
    return self._abnormal_close_order_list
